﻿#include <GL/glut.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

// 窗口设置
#define WIDTH 800
#define HEIGHT 600

// Pong游戏参数
#define PADDLE_WIDTH 15
#define PADDLE_HEIGHT 100
#define BALL_RADIUS 10
#define COURT_DEPTH 100

// 游戏状态变量
float paddle1Y = 0.0, paddle2Y = 0.0;
float ballX = 0.0, ballY = 0.0, ballZ = -COURT_DEPTH / 2;
float ballVelX = 0.5, ballVelY = 0.3, ballVelZ = 0.0;
int score1 = 0, score2 = 0;

// 初始化函数
void init(void) {
    // 设置清屏颜色为深蓝色
    glClearColor(0.0, 0.0, 0.2, 0.0);

    // 启用深度测试
    glEnable(GL_DEPTH_TEST);

    // 启用光照和材质
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_COLOR_MATERIAL);

    // 设置Gouraud着色模型
    glShadeModel(GL_SMOOTH);  // 关键：启用Gouraud着色

    // 设置光源属性
    GLfloat light_position[] = { 50.0, 50.0, 100.0, 1.0 };
    GLfloat light_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };

    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);

    // 设置材质属性
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 50.0 };
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
}

// 绘制3D球拍
void drawPaddle(float x, float y) {
    glPushMatrix();
    glTranslatef(x, y, 0.0);

    // 设置材质颜色
    if (x > 0) {
        glColor3f(0.0, 0.8, 0.0); // 右球拍：绿色
    }
    else {
        glColor3f(0.8, 0.0, 0.0); // 左球拍：红色
    }

    // 绘制3D球拍（使用GL_QUADS）
    glBegin(GL_QUADS);

    // 前面
    glNormal3f(0.0, 0.0, 1.0);
    glVertex3f(-PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);
    glVertex3f(PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);
    glVertex3f(PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);
    glVertex3f(-PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);

    // 后面
    glNormal3f(0.0, 0.0, -1.0);
    glVertex3f(-PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);
    glVertex3f(-PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);
    glVertex3f(PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);
    glVertex3f(PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);

    // 顶部
    glNormal3f(0.0, 1.0, 0.0);
    glVertex3f(-PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);
    glVertex3f(-PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);
    glVertex3f(PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);
    glVertex3f(PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);

    // 底部
    glNormal3f(0.0, -1.0, 0.0);
    glVertex3f(-PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);
    glVertex3f(PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);
    glVertex3f(PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);
    glVertex3f(-PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);

    // 左侧
    glNormal3f(-1.0, 0.0, 0.0);
    glVertex3f(-PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);
    glVertex3f(-PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);
    glVertex3f(-PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);
    glVertex3f(-PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);

    // 右侧
    glNormal3f(1.0, 0.0, 0.0);
    glVertex3f(PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);
    glVertex3f(PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, -PADDLE_WIDTH / 2);
    glVertex3f(PADDLE_WIDTH / 2, PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);
    glVertex3f(PADDLE_WIDTH / 2, -PADDLE_HEIGHT / 2, PADDLE_WIDTH / 2);

    glEnd();

    glPopMatrix();
}

// 绘制3D球
void drawBall() {
    glPushMatrix();
    glTranslatef(ballX, ballY, ballZ);

    // 设置球的材质颜色（黄色）
    glColor3f(1.0, 1.0, 0.0);

    // 使用GLUT提供的球体函数，自动应用Gouraud着色
    glutSolidSphere(BALL_RADIUS, 20, 20);

    glPopMatrix();
}

// 绘制游戏场地
void drawCourt() {
    glPushMatrix();

    // 设置场地材质颜色（蓝色）
    glColor3f(0.0, 0.0, 0.8);

    // 绘制地面
    glBegin(GL_QUADS);
    glNormal3f(0.0, 0.0, 1.0);
    glVertex3f(-300, -200, -COURT_DEPTH / 2);
    glVertex3f(300, -200, -COURT_DEPTH / 2);
    glVertex3f(300, 200, -COURT_DEPTH / 2);
    glVertex3f(-300, 200, -COURT_DEPTH / 2);
    glEnd();

    // 绘制边界
    glColor3f(0.8, 0.8, 0.8);  // 白色边界

    // 顶部边界
    glBegin(GL_QUADS);
    glNormal3f(0.0, -1.0, 0.0);
    glVertex3f(-300, 200, -COURT_DEPTH / 2);
    glVertex3f(300, 200, -COURT_DEPTH / 2);
    glVertex3f(300, 200, COURT_DEPTH / 2);
    glVertex3f(-300, 200, COURT_DEPTH / 2);
    glEnd();

    // 底部边界
    glBegin(GL_QUADS);
    glNormal3f(0.0, 1.0, 0.0);
    glVertex3f(-300, -200, -COURT_DEPTH / 2);
    glVertex3f(-300, -200, COURT_DEPTH / 2);
    glVertex3f(300, -200, COURT_DEPTH / 2);
    glVertex3f(300, -200, -COURT_DEPTH / 2);
    glEnd();

    // 右侧边界
    glBegin(GL_QUADS);
    glNormal3f(-1.0, 0.0, 0.0);
    glVertex3f(300, -200, -COURT_DEPTH / 2);
    glVertex3f(300, 200, -COURT_DEPTH / 2);
    glVertex3f(300, 200, COURT_DEPTH / 2);
    glVertex3f(300, -200, COURT_DEPTH / 2);
    glEnd();

    // 左侧边界
    glBegin(GL_QUADS);
    glNormal3f(1.0, 0.0, 0.0);
    glVertex3f(-300, -200, -COURT_DEPTH / 2);
    glVertex3f(-300, -200, COURT_DEPTH / 2);
    glVertex3f(-300, 200, COURT_DEPTH / 2);
    glVertex3f(-300, 200, -COURT_DEPTH / 2);
    glEnd();

    glPopMatrix();
}

// 更新游戏状态
void update(int value) {
    // 更新球的位置
    ballX += ballVelX;
    ballY += ballVelY;

    // 边界碰撞检测
    if (ballY > 200 - BALL_RADIUS || ballY < -200 + BALL_RADIUS) {
        ballVelY = -ballVelY;
    }

    // 球拍碰撞检测
    if (ballX > 290 - BALL_RADIUS && ballX < 290 + PADDLE_WIDTH / 2 &&
        ballY > paddle1Y - PADDLE_HEIGHT / 2 && ballY < paddle1Y + PADDLE_HEIGHT / 2) {
        ballVelX = -ballVelX;
        // 碰撞角度调整
        ballVelY += (ballY - paddle1Y) * 0.05;
    }

    if (ballX < -290 + BALL_RADIUS && ballX > -290 - PADDLE_WIDTH / 2 &&
        ballY > paddle2Y - PADDLE_HEIGHT / 2 && ballY < paddle2Y + PADDLE_HEIGHT / 2) {
        ballVelX = -ballVelX;
        // 碰撞角度调整
        ballVelY += (ballY - paddle2Y) * 0.05;
    }

    // 得分检测
    if (ballX > 300) {
        score2++;
        ballX = 0;
        ballY = 0;
        ballVelX = -fabs(ballVelX);
    }

    if (ballX < -300) {
        score1++;
        ballX = 0;
        ballY = 0;
        ballVelX = fabs(ballVelX);
    }

    // 限制球的速度
    float speed = sqrt(ballVelX * ballVelX + ballVelY * ballVelY);
    if (speed > 1.5) {
        ballVelX = ballVelX * 1.5 / speed;
        ballVelY = ballVelY * 1.5 / speed;
    }

    // 重绘场景
    glutPostRedisplay();
    glutTimerFunc(16, update, 0);  // 约60FPS
}

// 键盘控制
void keyboard(unsigned char key, int x, int y) {
    switch (key) {
    case 'w': case 'W':
        paddle2Y += 15;
        break;
    case 's': case 'S':
        paddle2Y -= 15;
        break;
    case 'i': case 'I':
        paddle1Y += 15;
        break;
    case 'k': case 'K':
        paddle1Y -= 15;
        break;
   
    }

    // 限制球拍移动范围
    paddle1Y = fmaxf(fminf(paddle1Y, 150), -150);
    paddle2Y = fmaxf(fminf(paddle2Y, 150), -150);
}

// 显示回调函数
void display(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 设置视角
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 400.0,  // 眼睛位置
        0.0, 0.0, 0.0,    // 观察点
        0.0, 1.0, 0.0);   // 向上向量

    // 绘制场景
    drawCourt();
    drawPaddle(290, paddle1Y);
    drawPaddle(-290, paddle2Y);
    drawBall();

    // 绘制分数
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(-400, 400, -300, 300);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    char scoreText[20];
    sprintf_s(scoreText, "%d - %d", score1, score2);  // 使用sprintf（添加了strlen.h）
    glColor3f(1.0, 1.0, 1.0);
    glRasterPos2f(-20, 280);

    // 修复有符号/无符号不匹配问题
    for (int i = 0; i < (int)strlen(scoreText); i++) {
        glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, scoreText[i]);
    }

    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);

    glutSwapBuffers();
}

// 窗口大小改变回调
void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 1000.0);
    glMatrixMode(GL_MODELVIEW);
}

// 主函数
int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(WIDTH, HEIGHT);
    glutCreateWindow("Gouraud光照模型实验 - Pong游戏");

    init();

    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutTimerFunc(16, update, 0);

    glutMainLoop();
    return 0;
}